package com.wujunshen.dumpling.core.process;

import com.wujunshen.dumpling.core.Configuration;
import com.wujunshen.dumpling.core.engine.EngineFileType;
import com.wujunshen.dumpling.database.metadata.IColumn;
import com.wujunshen.dumpling.database.metadata.IPrimaryKey;
import com.wujunshen.dumpling.database.metadata.ITable;
import com.wujunshen.dumpling.database.metadata.model.Data;
import com.wujunshen.dumpling.database.metadata.model.Table;
import com.wujunshen.dumpling.common.utils.Assert;
import com.wujunshen.dumpling.common.utils.BeanUtils;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

import static com.wujunshen.dumpling.common.utils.BeanUtils.*;

/**
 * AbstractBuilder
 *
 * @author frankwoo
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class AbstractProcess {
  /** Configuration */
  Configuration config;
  /** 表信息缓存 */
  volatile Map<String, List<? extends ITable>> tablesCaching = new ConcurrentHashMap<>(16);
  /** 列信息缓存 */
  volatile Map<String, List<IColumn>> columnsCaching = new ConcurrentHashMap<>(16);
  /** 主键信息缓存 */
  volatile Map<String, List<IPrimaryKey>> primaryKeysCaching = new ConcurrentHashMap<>(16);

  /**
   * 构造方法
   *
   * @param configuration {@link Configuration}
   */
  AbstractProcess(Configuration configuration) {
    Assert.isNotNull(configuration);
    this.config = configuration;
  }

  /**
   * 过滤表 存在指定生成和指定不生成，优先级为：如果指定生成，只会生成指定的表，未指定的不会生成，也不会处理忽略表
   *
   * @param tables {@link List} 处理前数据
   * @return {@link List} 处理过后的数据
   */
  List<Table> filterTables(List<Table> tables) {
    if (Objects.isNull(config.getProduceConfig())) {
      return tables;
    }
    ProcessConfig produceConfig = config.getProduceConfig();
    // 指定生成的表名、前缀、后缀任意不为空，按照指定表生成，其余不生成，不会在处理忽略表
    if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableName())
        || CollectionUtils.isNotEmpty(produceConfig.getDesignatedTablePrefix())
        || CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableSuffix())) {
      return handleDesignated(tables);
    } else { // 处理忽略表
      return handleIgnore(tables);
    }
  }

  /**
   * 处理指定表
   *
   * @param tables {@link List} 处理前数据
   * @return {@link List} 处理过后的数据
   */
  private List<Table> handleDesignated(List<Table> tables) {
    ProcessConfig produceConfig = config.getProduceConfig();
    if (Objects.isNull(produceConfig)) {
      return new ArrayList<>();
    }
    List<Table> tableList = new ArrayList<>();
    // 指定表名
    if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableName())) {
      produceConfig
          .getDesignatedTableName()
          .forEach(
              name ->
                  tableList.addAll(
                      tables.stream()
                          .filter(table -> table.getTableName().equals(name))
                          .collect(Collectors.toList())));
    }
    // 指定表名前缀
    if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTablePrefix())) {
      produceConfig
          .getDesignatedTablePrefix()
          .forEach(
              prefix ->
                  tableList.addAll(
                      tables.stream()
                          .filter(table -> table.getTableName().startsWith(prefix))
                          .collect(Collectors.toList())));
    }
    // 指定表名后缀
    if (CollectionUtils.isNotEmpty(produceConfig.getDesignatedTableSuffix())) {
      produceConfig
          .getDesignatedTableSuffix()
          .forEach(
              suffix ->
                  tableList.addAll(
                      tables.stream()
                          .filter(table -> table.getTableName().endsWith(suffix))
                          .collect(Collectors.toList())));
    }
    return tableList;
  }

  /**
   * 处理忽略
   *
   * @param tables {@link List} 处理前数据
   * @return {@link List} 处理过后的数据
   */
  private List<Table> handleIgnore(List<Table> tables) {
    ProcessConfig produceConfig = config.getProduceConfig();
    if (Objects.isNull(produceConfig)) {
      return tables;
    }
    // 处理忽略表名
    if (CollectionUtils.isNotEmpty(produceConfig.getIgnoreTableName())) {
      List<String> ignoreTableNameList = produceConfig.getIgnoreTableName();

      for (String name : ignoreTableNameList) {
        tables =
            tables.stream()
                .filter(table -> !table.getTableName().equals(name))
                .collect(Collectors.toList());
      }
    }
    // 忽略表名前缀
    if (CollectionUtils.isNotEmpty(produceConfig.getIgnoreTablePrefix())) {
      List<String> list = produceConfig.getIgnoreTablePrefix();
      for (String prefix : list) {
        tables =
            tables.stream()
                .filter(table -> !table.getTableName().startsWith(prefix))
                .collect(Collectors.toList());
      }
    }
    // 忽略表名后缀
    if (CollectionUtils.isNotEmpty(produceConfig.getIgnoreTableSuffix())) {
      List<String> list = produceConfig.getIgnoreTableSuffix();
      for (String suffix : list) {
        tables =
            tables.stream()
                .filter(table -> !table.getTableName().endsWith(suffix))
                .collect(Collectors.toList());
      }
    }
    return tables;
  }

  /**
   * 优化数据
   *
   * @param data {@link Data}
   */
  void optimizeData(Data data) {
    // trim
    beanAttributeValueTrim(data);
    // tables
    List<Table> tableList = data.getTables();
    // columns
    tableList.forEach(
        table -> {
          // table escape xml
          beanAttributeValueTrim(table);
          // columns escape xml
          table.getColumns().forEach(BeanUtils::beanAttributeValueTrim);
        });
    // if file type is word
    if (config.getEngineConfig().getFileType().equals(EngineFileType.WORD)) {
      // escape xml
      beanAttributeValueEscapeXml(data);
      // tables
      tableList.forEach(
          table -> {
            // table escape xml
            beanAttributeValueEscapeXml(table);
            // columns escape xml
            table.getColumns().forEach(BeanUtils::beanAttributeValueEscapeXml);
          });
    }
    // if file type is markdown
    if (config.getEngineConfig().getFileType().equals(EngineFileType.MD)) {
      // escape xml
      beanAttributeValueReplaceBlank(data);
      // columns
      tableList.forEach(
          table -> {
            // table escape xml
            beanAttributeValueReplaceBlank(table);
            // columns escape xml
            table.getColumns().forEach(BeanUtils::beanAttributeValueReplaceBlank);
          });
    }
  }

  /**
   * 处理
   *
   * @return {@link Data}
   */
  public abstract Data process();
}
